package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

@Activate(group = {CommonConstants.CONSUMER})
public class TpMonitorFilter implements Filter {

    //原始数据
    private static Map<String, Queue<Long>> origData = new ConcurrentHashMap<>();

    private static Object lock = new Object();

    private static long perCountTime = 20000l;

    private static AtomicInteger invokeCount = new AtomicInteger(0);
    //启动时，启动线程
    public void init(){
        Thread checkThread = new Thread(new CheckDeque());
        checkThread.start();
        Thread delThread = new Thread(new DelDeque());
        delThread.start();
        Thread outThread = new Thread(new OutThread());
        outThread.start();
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        int i = invokeCount.addAndGet(1);
        if(i == 1){
            init();
        }
        String methodName = invocation.getMethodName();
        long startTime = System.currentTimeMillis();
        Result result = invoker.invoke(invocation);
        long endTime = System.currentTimeMillis();
        //DCL机制
        if (origData.get(methodName) == null) {
            synchronized (origData){
                if (origData.get(methodName) == null) {
                    Queue<Long> queue = new ConcurrentLinkedQueue<>();
                    queue.add(endTime - startTime);
                    origData.put(methodName,queue);
                }
            }
        }else{
            Queue<Long> queue = origData.get(methodName);
            queue.add(endTime - startTime);
        }
        return result;
    }

    private static class CheckDeque implements Runnable{
        @Override
        public void run() {
            try {
                while (true){
                    //每隔5s,检查map中的队列，队尾插入一个标记
                    for (Map.Entry<String, Queue<Long>> entry : origData.entrySet()) {
                        if (entry.getValue() != null) {
                            Queue<Long> queue = entry.getValue();
                            queue.add(-1l);
                            System.out.println("到达时间，插入一个标志-1l");
                        }
                    }
                    Thread.sleep(5000l);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static class DelDeque implements Runnable{

        @Override
        public void run() {
            //一分钟后，每隔5s，删除一次队列头部第一个标记之前的数据，启动一分钟以后开始进行
            try {
                Thread.sleep(perCountTime);
                System.out.println("删除线程开始工作！");
                while (true){
                    synchronized (lock) {
                        for (Map.Entry<String, Queue<Long>> entry : origData.entrySet()) {
                            if (entry.getValue() != null) {
                                Queue<Long> queue = entry.getValue();
                                //同时删除队列头部的标记及之前的数据
                                System.out.print("删除前总个数："+queue.size()+",del：");
                                int count = 0;
                                while (true) {
                                    count++;
                                    if (queue.peek() != -1l) {
                                        //如果不是-1说明是执行时间，直接弹出
                                        System.out.print(queue.peek() + ",");
                                        queue.poll();
                                    } else {
                                        //如果是-1，说明此处是5s的数据，弹出-1并退出循环
                                        System.out.println("到达"+queue.peek()+"边界，删除");
                                        queue.poll();
                                        break;
                                    }
                                }
                                System.out.println("删除个数："+count);
                            }
                        }
                    }
                    Thread.sleep(5000l);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static class OutThread implements Runnable{
        @Override
        public void run() {
            List<Long> list = null;
            try {
                while (true){
                    for (Map.Entry<String, Queue<Long>> entry : origData.entrySet()) {
                        synchronized (lock) {
                            try {
                                if (entry.getValue() != null) {
                                    Queue<Long> queue = entry.getValue();
                                    Queue<Long> copy = new ConcurrentLinkedQueue(queue);
                                    list = new ArrayList<>();
                                    Long[] array = copy.toArray(new Long[0]);
                                    for (int i = 0; i < array.length; i++) {
                                        if (array[i] != -1l) {
                                            list.add(array[i]);
                                        }
                                    }
                                    if(list.size() != 0){
                                        Collections.sort(list);
                                        Long round = Math.round(0.9 * list.size());
                                        System.out.println("当前list个数：" + list.size()+",tp90 round:"+round);
                                        Long tp90 = list.get(round.intValue());
                                        round = Math.round(0.99 * list.size());
                                        System.out.println("当前list个数：" + list.size()+",tp99 round:"+round);
                                        Long tp99 = list.get(round.intValue() -  1);
                                        System.out.println("current methodName:" + entry.getKey() + ",TP90:" + tp90 + ",TP99:" + tp99);
                                    }else{
                                        System.out.println("list为空，没有请求，跳过最近统计");
                                    }
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                                continue;
                            }
                        }
                    }
                    Thread.sleep(5000l);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
